const CANVAS_HEIGHT = 1024;

// Vertex shader program
const VSHADER_SOURCE =
	"attribute vec4 a_Position;\n" +
	"attribute vec4 a_Color;\n" +
	"varying vec4 v_Color;\n" +
	"void main() {\n" +
	"  gl_Position = a_Position;\n" +
	"  v_Color = a_Color;\n" +
	"}\n";

// Fragment shader program
const FSHADER_SOURCE =
	"#ifdef GL_ES\n" +
	"precision mediump float;\n" +
	"#endif\n" +
	"varying vec4 v_Color;\n" +
	"void main() {\n" +
	"  gl_FragColor = v_Color;\n" +
	"}\n";

const CLEARCOLOR = [0.0, 0.0, 0.0, 1.0];

export default class InterPic {
	constructor(viewer) {
		this.viewer = viewer;
		this.isLoadData = false;
		this.dataInfo = {
			LonMin: 0,
			LonMax: 0,
			LatMin: 0,
			LatMax: 0,
			DLon: 0,
			DLat: 0,
			LonNums: 0,
			LatNums: 0,
			LonStep: 0,
			LatStep: 0,
			UV: [], // UV分量 [i][j]->([i][j][0]-U [i][j][1]-V [i][j][2]-Speed)

			// 色斑图绘图区设置
			picWidth: 0,
			picHeight: 0,
			picXStep: 0,
			picYStep: 0,
		};

		// todo 待传入
		this.picInfo = {
			valueAry: [],
			rgbAry: [],
		};
		this.picInfo.valueAry.reverse();
		this.picInfo.rgbAry.reverse();

		this.rectangle = null;
		this.range = []; //四角坐标(地球背面)
		this.image = null;
	}

	// 处理风场数据，计算风速
	loadDataInfo(param) {
		this.isLoadData = false;
		const { dataInfo, colorInfo, isWindy = false } = param;
		if (
			dataInfo.Bound.length < 7 ||
			dataInfo.Bound[2] < 2 ||
			dataInfo.Bound[3] < 2 ||
			dataInfo.Bound[4] < 0 ||
			dataInfo.Bound[5] < 0
		) {
			return;
		}

		if (
			isWindy &&
			dataInfo.DataAry.length !==
				2 * dataInfo.Bound[2] * dataInfo.Bound[3]
		) {
			return;
		}

		// ******************************************************************************
		// 初始化数据信息
		this.isWindy = isWindy;
		this.dataInfo.LonMin = dataInfo.Bound[0];
		this.dataInfo.LatMin = dataInfo.Bound[1];
		this.dataInfo.LonNums = dataInfo.Bound[2];
		this.dataInfo.LatNums = dataInfo.Bound[3];
		this.dataInfo.LonStep = dataInfo.Bound[4] / (dataInfo.Bound[2] - 1);
		this.dataInfo.LatStep = dataInfo.Bound[5] / (dataInfo.Bound[3] - 1);

		this.dataInfo.DLon = dataInfo.Bound[4];
		this.dataInfo.DLat = dataInfo.Bound[5];
		this.dataInfo.LonMax = dataInfo.Bound[0] + dataInfo.Bound[4];
		this.dataInfo.LatMax = dataInfo.Bound[1] + dataInfo.Bound[5];
		this.dataInfo.ValueScale = 1.0 / dataInfo.Bound[6];

		const range = [
			this.dataInfo.LonMin,
			this.dataInfo.LatMin,
			this.dataInfo.LonMax,
			this.dataInfo.LatMax,
		];
		if (range.toString() !== this.range.toString()) {
			this.range = range;
			this.initPic("interPic", "");
		}

		this.dataInfo.picHeight = CANVAS_HEIGHT;
		this.dataInfo.picWidth = Math.ceil(
			(CANVAS_HEIGHT * this.dataInfo.DLon) / this.dataInfo.DLat
		);
		this.dataInfo.picXStep =
			this.dataInfo.picWidth / (this.dataInfo.LonNums - 1);
		this.dataInfo.picYStep =
			this.dataInfo.picHeight / (this.dataInfo.LatNums - 1);
		this.dataInfo.UV = [];

		if (isWindy) {
			this.dataInfo.DataAry = [];
			for (let index = 0; index < this.dataInfo.LatNums; index++) {
				this.dataInfo.UV[index] = [];
				for (let j = 0; j < this.dataInfo.LonNums; j++) {
					this.dataInfo.UV[index].push(
						this._getUVSpeed(
							dataInfo.DataAry[
								index * this.dataInfo.LonNums * 2 + j * 2
							],
							dataInfo.DataAry[
								index * this.dataInfo.LonNums * 2 + j * 2 + 1
							]
						)
					);
				}
			}
		} else {
			this.dataInfo.UV = [];
			this.dataInfo.DataAry = dataInfo.DataAry;
		}

		// 初始化配色信息
		if (colorInfo.Values?.length) {
			if (colorInfo.Values[0] > colorInfo.Values[1]) {
				colorInfo.Values.reverse();
				colorInfo.Colors.reverse();
			}
			this.picInfo.valueAry = colorInfo.Values;
			this.picInfo.rgbAry = colorInfo.Colors;
		} else {
			this.picInfo.valueAry = [];
			this.picInfo.rgbAry = [];
		}

		this.isLoadData = true;
	}

	_getUVSpeed(u, v) {
		return [u, v, Math.sqrt(u * u + v * v)];
	}

	// 添加色斑图
	addInterPic_webgl(alpha = 255) {
		this.drawType = "webgl";
		console.time("webgl");
		const c = document.createElement("canvas");

		const width = this.dataInfo.picWidth;
		const height = this.dataInfo.picHeight;
		const xNum = this.dataInfo.LatNums;
		const yNum = this.dataInfo.LonNums;
		const xStep = 2 / (yNum - 1);
		const yStep = 2 / (xNum - 1);
		c.width = width;
		c.height = height;
		let positionArr = [];
		let indicesArr = [];

		for (let row = 0; row < xNum; row++) {
			for (let col = 0; col < yNum; col++) {
				const x = -1 + col * xStep;
				const y = -1 + row * yStep;
				const index = row * yNum + col;
				const value = this.dataInfo.DataAry[index];
				const rgba = this._getInterColor(value, alpha);
				positionArr.push(
					x,
					y,
					rgba[0] / 255,
					rgba[1] / 255,
					rgba[2] / 255
				);
				if (row < xNum - 1 && col < yNum - 1) {
					indicesArr.push(index + 0, index + yNum + 1, index + yNum);
					indicesArr.push(index + 0, index + 1, index + yNum + 1);
				}
			}
		}

		this.initGL(c); //初始化webgl
		this.initVertexBuffers(this.gl, positionArr, indicesArr);

		// const ctx = c.getContext("2d");

		// const myImgData = ctx.createImageData(width, height);
		// const len = myImgData.data.length;

		// for (let index = 0; index < len; index += 4) {
		// 	const y = Math.floor(index / 4 / width); //绘图区xy
		// 	const x = (index / 4) % width;
		// 	const py = (height - y) / yStep; //数据信息
		// 	const px = x / xStep;
		// 	const interValue = this._interBilinear(px, py);
		// 	if (!interValue) {
		// 		continue;
		// 	}
		// 	const rgba = this._getInterColor(interValue, alpha);
		// 	myImgData.data[index] = rgba[0];
		// 	myImgData.data[index + 1] = rgba[1];
		// 	myImgData.data[index + 2] = rgba[2];
		// 	myImgData.data[index + 3] = rgba[3];
		// }
		// ctx.putImageData(myImgData, 0, 0);
		// 更新色斑图
		this.updatePic(c);
		console.timeEnd("webgl");
	}

	// 添加色斑图
	addInterPic_canvas(alpha = 255) {
		this.drawType = "canvas";
		console.time("canvas");
		const c = document.createElement("canvas");

		const width = this.dataInfo.picWidth;
		const height = this.dataInfo.picHeight;
		const xStep = this.dataInfo.picXStep;
		const yStep = this.dataInfo.picYStep;
		c.width = width;
		c.height = height;
		const ctx = c.getContext("2d");

		const myImgData = ctx.createImageData(width, height);
		const len = myImgData.data.length;

		for (let index = 0; index < len; index += 4) {
			const y = Math.floor(index / 4 / width); //绘图区xy
			const x = (index / 4) % width;
			const py = (height - y) / yStep; //数据信息
			const px = x / xStep;
			const interValue = this._interBilinear(px, py);
			if (!interValue) {
				continue;
			}
			const rgba = this._getInterColor(interValue, alpha);
			myImgData.data[index] = rgba[0];
			myImgData.data[index + 1] = rgba[1];
			myImgData.data[index + 2] = rgba[2];
			myImgData.data[index + 3] = rgba[3];
		}
		ctx.putImageData(myImgData, 0, 0);
		// 更新色斑图
		this.updatePic(c);
		console.timeEnd("canvas");
	}

	//初始化GL
	initGL(canvas) {
		// Get the rendering context for WebGL
		var gl = getWebGLContext(canvas);
		this.gl = gl;
		if (!gl) {
			console.log("Failed to get the rendering context for WebGL");
			return;
		}

		// Initialize shaders
		if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
			console.log("Failed to intialize shaders.");
			return;
		}

		// Set the vertex coordinates and color
		// var n = this.initVertexBuffers(gl);
		// if (n < 0) {
		// 	console.log("Failed to set the vertex information");
		// 	return;
		// }

		// Set clear color and enable hidden surface removal
		gl.clearColor(...CLEARCOLOR);
	}

	//gl绘制
	initVertexBuffers(gl, verti, ind) {
		// Clear color buffer
		gl.clear(gl.COLOR_BUFFER_BIT);

		//顶点信息数组顺序：x,y,r,g,b,a 五个一组
		var verticesColors = new Float32Array(verti);

		// Indices of the vertices
		var indices = new Uint32Array(ind);

		// Create a buffer object
		var vertexColorBuffer = gl.createBuffer();
		var indexBuffer = gl.createBuffer();
		if (!vertexColorBuffer || !indexBuffer) {
			return -1;
		}

		// Write the vertex coordinates and color to the buffer object
		gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer);
		gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);

		var FSIZE = verticesColors.BYTES_PER_ELEMENT;
		// Assign the buffer object to a_Position and enable the assignment
		var a_Position = gl.getAttribLocation(gl.program, "a_Position");
		if (a_Position < 0) {
			console.log("Failed to get the storage location of a_Position");
			return -1;
		}
		gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 5, 0);
		gl.enableVertexAttribArray(a_Position);
		// Assign the buffer object to a_Color and enable the assignment
		var a_Color = gl.getAttribLocation(gl.program, "a_Color");
		if (a_Color < 0) {
			console.log("Failed to get the storage location of a_Color");
			return -1;
		}
		gl.vertexAttribPointer(
			a_Color,
			3,
			gl.FLOAT,
			false,
			FSIZE * 5,
			FSIZE * 2
		);
		gl.enableVertexAttribArray(a_Color);

		// Write the indices to the buffer object
		gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
		gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);

		// Draw the cube
		const n = indices.length;
		gl.getExtension("OES_element_index_uint");
		gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_INT, 0);
	}

	// 初始化图片
	initPic(id, image) {
		// Primitive方式
		const instance = new Cesium.GeometryInstance({
			geometry: new Cesium.RectangleGeometry({
				rectangle: Cesium.Rectangle.fromDegrees(...this.range),
				vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT,
			}),
		});
		const primitive = new Cesium.Primitive({
			geometryInstances: instance,
			appearance: new Cesium.EllipsoidSurfaceAppearance({
				material: new Cesium.Material({
					fabric: {
						type: "Image",
						uniforms: {
							image,
						},
					},
				}),
			}),
		});
		this.rectangle = this.viewer.addPrimitive(id, primitive);
	}

	// 更新图片
	updatePic(image) {
		this.rectangle.appearance.material = new Cesium.Material({
			fabric: {
				type: "Image",
				uniforms: {
					image,
				},
			},
		});
	}

	// 更新图片配色
	updataPicColor(colors) {
		this.picInfo.rgbAry = colors;
		if (this.drawType === "webgl") {
			this.addInterPic_webgl();
		} else {
			this.addInterPic_canvas();
		}
	}

	// 插值
	_interBilinear(px, py) {
		const rowIndex = Math.floor(py);
		const colIndex = Math.floor(px);
		const rowIndex2 = rowIndex + 1;
		const colIndex2 = colIndex + 1;
		if (
			rowIndex < 0 ||
			rowIndex >= this.dataInfo.LatNums ||
			colIndex < 0 ||
			colIndex >= this.dataInfo.LonNums ||
			rowIndex2 < 0 ||
			rowIndex2 >= this.dataInfo.LatNums ||
			colIndex2 < 0 ||
			colIndex2 >= this.dataInfo.LonNums
		) {
			return null;
		}

		let g00;
		let g01;
		let g10;
		let g11;
		let result;

		const x = px - colIndex;
		const y = py - rowIndex;
		const rx = 1 - x;
		const ry = 1 - y;
		const a = rx * ry;
		const b = x * ry;
		const c = rx * y;
		const d = x * y;

		if (this.isWindy) {
			g00 = this.dataInfo.UV[rowIndex][colIndex];
			g01 = this.dataInfo.UV[rowIndex2][colIndex];
			g10 = this.dataInfo.UV[rowIndex][colIndex2];
			g11 = this.dataInfo.UV[rowIndex2][colIndex2];

			const u = g00[0] * a + g10[0] * b + g01[0] * c + g11[0] * d;
			const v = g00[1] * a + g10[1] * b + g01[1] * c + g11[1] * d;
			const s = u * u + v * v;
			result = Math.sqrt(s);
		} else {
			const cols = this.dataInfo.LonNums;
			g00 = this.dataInfo.DataAry[rowIndex * cols + colIndex];
			g01 = this.dataInfo.DataAry[rowIndex2 * cols + colIndex];
			g10 = this.dataInfo.DataAry[rowIndex * cols + colIndex2];
			g11 = this.dataInfo.DataAry[rowIndex2 * cols + colIndex2];
			result = g00 * a + g10 * b + g01 * c + g11 * d;
		}

		return result;
	}

	// 获取渐变色
	_getInterColor(value, a) {
		if (this.picInfo.valueAry === null) {
			return [0, 0, 0, 0];
		}
		const valueAry = this.picInfo.valueAry;
		const rgbAry = this.picInfo.rgbAry;

		const len = valueAry.length;
		const maxValue = valueAry[len - 1];
		const minValue = valueAry[0];

		let index = 0;
		let scale = 0;
		let r = 0,
			g = 0,
			b = 0;
		let r2 = 0,
			g2 = 0,
			b2 = 0;

		if (value > minValue && value <= maxValue) {
			for (let i = 1; i < len; i++) {
				if (value <= valueAry[i]) {
					index = i;
					scale =
						(value - valueAry[i - 1]) /
						(valueAry[i] - valueAry[i - 1]);
					break;
				}
			}
			r = rgbAry[index - 1][0];
			g = rgbAry[index - 1][1];
			b = rgbAry[index - 1][2];

			r2 = rgbAry[index][0];
			g2 = rgbAry[index][1];
			b2 = rgbAry[index][2];

			r = r + scale * (r2 - r);
			g = g + scale * (g2 - g);
			b = b + scale * (b2 - b);
		} else if (value > maxValue) {
			r = rgbAry[len - 1][0];
			g = rgbAry[len - 1][1];
			b = rgbAry[len - 1][2];
		} else if (value <= minValue) {
			return [0, 0, 0, 0];
		}
		return [r, g, b, a];
	}
}
